using System;
using System.Collections.Generic;
using PicoContainer.Defaults;
using XEvolve.Core.Runtime;
using XEvolve.Core.SPI;

namespace XEvolve.Core.Pico
{
    internal class PicoBasedExtensionRegistry : SPI.IExtensionRegistry, Runtime.IExtensionRegistry
    {
        private readonly DefaultPicoContainer context;
        private readonly IDictionary<object, PicoBasedExtensionPoint> extensionPoints;

        public PicoBasedExtensionRegistry(DefaultPicoContainer context)
        {
            this.context = context;
            extensionPoints = new Dictionary<object, PicoBasedExtensionPoint>();
        }

        public void RegisterExtensionPoint(object identifier, bool isCaching)
        {
            if (extensionPoints.ContainsKey(identifier)) throw new ExtensionPointException("Extenion Point " + identifier + " is already registered");
            extensionPoints[identifier] = new PicoBasedExtensionPoint(new DefaultPicoContainer(new XComponentAdapterFactory(isCaching)) {Parent = context});
        }

        private PicoBasedExtensionPoint GetExtensionPointInternal(object extensionPointIdentifier)
        {
            if (!extensionPoints.ContainsKey(extensionPointIdentifier)) throw new ExtensionPointException("Extenion Point " + extensionPointIdentifier + " is not found");
            return extensionPoints[extensionPointIdentifier];
        }

        public void RegisterExtension(object extensionPointIdentifier, object extensionIdentifier, object extensionInstance)
        {
            GetExtensionPointInternal(extensionPointIdentifier).RegisterExtension(extensionIdentifier, extensionInstance);
        }

        public void RegisterExtension(object extensionPointIdentifier, object extensionIdentifier, Type extensionType)
        {
            GetExtensionPointInternal(extensionPointIdentifier).RegisterExtension(extensionIdentifier, extensionType);
        }

        public void RegisterExtension(object extensionPointIdentifier, object extensionInstance)
        {
            RegisterExtension(extensionPointIdentifier, Guid.NewGuid(), extensionInstance);
        }

        public void RegisterExtension(object extensionPointIdentifier, Type extensionType)
        {
            RegisterExtension(extensionPointIdentifier, Guid.NewGuid(), extensionType);
        }

        public IExtensionPoint<ExtensionType> GetExtensionPoint<ExtensionType>(Plugin.ExtensionPoint<ExtensionType> extensionPoint)
        {
            return new ExtensionPoint<ExtensionType>(GetExtensionPointInternal(extensionPoint.Identifier));
        }

        public IExtensionPoint<ExtensionIdentifierType, ExtensionType> GetExtensionPoint<ExtensionIdentifierType, ExtensionType>(Plugin.ExtensionPoint<ExtensionIdentifierType, ExtensionType> extensionPoint)
        {
            return new ExtensionPoint<ExtensionIdentifierType, ExtensionType>(GetExtensionPointInternal(extensionPoint.Identifier));
        }

        private class ExtensionPoint<ExtensionType> : IExtensionPoint<ExtensionType>
        {
            private readonly PicoBasedExtensionPoint extensionPoint;

            public ExtensionPoint(PicoBasedExtensionPoint extensionPoint)
            {
                this.extensionPoint = extensionPoint;
            }

            public ExtensionType[] Extensions
            {
                get { return extensionPoint.GetExtensions<ExtensionType>(); }
            }
        }

        private class ExtensionPoint<ExtensionIdentifierType, ExtensionType> : IExtensionPoint<ExtensionIdentifierType, ExtensionType>
        {
            private readonly PicoBasedExtensionPoint extensionPoint;

            public ExtensionPoint(PicoBasedExtensionPoint extensionPoint)
            {
                this.extensionPoint = extensionPoint;
            }

            public ExtensionType[] Extensions
            {
                get { return extensionPoint.GetExtensions<ExtensionType>(); }
            }

            public ExtensionIdentifierType[] ExtensionIdentifiers
            {
                get { return extensionPoint.GetExtensionIdentifiers<ExtensionIdentifierType>(); }
            }

            public ExtensionType GetExtension(ExtensionIdentifierType extensionIdentifier)
            {
                return extensionPoint.GetExtension<ExtensionType>(extensionIdentifier);
            }
        }
    }
}